package com.yuandian.business.monitor.documentExport;

import com.yuandian.api.management.entity.ConfDocumentExportInfo;
import com.yuandian.api.management.enums.DocumentHandleStateType;
import com.yuandian.api.management.feign.RemoteAsynchronousDownloadService;
import com.yuandian.bpm.common.core.constant.SecurityConstants;
import com.yuandian.enums.DocumentHandlerType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * 文档导出申请处理中心，大致处理流程如下：
 * 1、进程启动，服务初始化(检查目录，获取任务并行度，初始化业务线程池)
 * 2、检查线程，获取导出处理请求，进入队列；
 * 3、工作线程，获取导出处理请求，进行处理，并执行业务下载处理
 *
 *
 *
 * @author User
 *
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class DocumentExportHandlerCenter implements Runnable, ApplicationContextAware {

	private static ApplicationContext applicationContext;

	/**
	 * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量
	 *
	 * @param applicationContext
	 * @throws BeansException
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		DocumentExportHandlerCenter.applicationContext = applicationContext;
	}

	private final RemoteAsynchronousDownloadService remoteAsynchronousDownloadService;

	/**
	 * 当前应用程序，启动目录
	 */
	private final String user_path = "user.dir";
	/**
	 * 文档生成存放路径名称
	 */
	private final String doc_path = "docs";
	/**
	 *  文档生成存放基础路径
	 */
	private final String basePath =  System.getProperty(user_path)+File.separator+doc_path;
	/**
	 * 处理线程个数
	 */
	private int workers = 4;

    /**
     * 该进程服务名称
     */
	private String serviceCode;

    @Value("${server.servlet.context-path:business-monitor}")
    public void setServiceCode(String serviceCode) {
    	if(serviceCode!=null && !"".equals(serviceCode)){
    		this.serviceCode = serviceCode.substring(0, serviceCode.length());
    	}else{
    		throw new IllegalAccessError("该服务路径没有进行设置");
    	}
    }

    @Value("${docs.export.handlers:4}")
    public void setWorkers(int workers) {
        this.workers = workers;
    }
    private ExecutorService documentExportHandlerService;
	/**
	 * 初始化环境
	 */
	@PostConstruct
	public void initEnv(){
		// 1、查看路径是否存在
		checkDocsBasePathAndTmpDocs();
		// 2、构建文档导出处理线程池
		// 2、构建文档导出处理线程池
		buildDocumentExportHandlerService();
		// 3、监听文档导出申请
		Thread documentExportListener = new Thread(this);
		documentExportListener.setName("文档导出申请检查者");
		documentExportListener.setDaemon(false);
		documentExportListener.start();
	}
	/**
	 * 检查文件存放路径是否存在，以及路临时文件清理
	 */
	public void checkDocsBasePathAndTmpDocs(){
		File file = new File(basePath);
		if(!file.exists()){
			file.mkdirs();
		}
	}


	/**
	 * 构建文档导出处理线程池
	 */
	public void buildDocumentExportHandlerService(){
		this.documentExportHandlerService = Executors.newFixedThreadPool(workers, new ThreadFactory() {

			private  ThreadGroup group = (System.getSecurityManager() != null) ?
					System.getSecurityManager().getThreadGroup() : Thread.currentThread().getThreadGroup();
			private AtomicInteger threadNumber = new AtomicInteger(1);
			private  String namePrefix = "文档导出工作者-";

			@Override
			public Thread newThread(Runnable r) {
				Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
				if (t.isDaemon())
					t.setDaemon(false);
				if (t.getPriority() != Thread.NORM_PRIORITY)
					t.setPriority(Thread.NORM_PRIORITY);
				return t;
			}
		});
	}




	@Override
	public void run() {
		documentHandleInfoMonit();
	}

	// 容器大小
	public int queuesize = 100;
	// 文档导出申请队列
	public PriorityBlockingQueue<ConfDocumentExportInfo> documentHandleInfoQueue = new PriorityBlockingQueue<>(queuesize,new Comparator<ConfDocumentExportInfo>() {
		@Override
		public int compare(ConfDocumentExportInfo documentHandleInfo1, ConfDocumentExportInfo documentHandleInfo2) {
			LocalDateTime submitTime1 = documentHandleInfo1.getSubmitTime();
			LocalDateTime submitTime2 = documentHandleInfo2.getSubmitTime();
			return submitTime2.getNano() >= submitTime1.getNano()?1:-1;
		}
	});
	@SuppressWarnings("static-access")
	public void documentHandleInfoMonit(){
		// 任务处理结果集合
		List<Future<Void>> futureContainer = new ArrayList<>();
		// 1、检查处于“处理中,以及在队列中”状态任务，优先进行处理(根据当前)
		List<ConfDocumentExportInfo> documentHandleInfos = new ArrayList<>();

		ConfDocumentExportInfo confDocumentExportInfo1 = new ConfDocumentExportInfo();
		confDocumentExportInfo1.setServiceCode(this.serviceCode);
		confDocumentExportInfo1.setDocHandleState(DocumentHandleStateType.PROCESSING.getType());

		documentHandleInfos.addAll(remoteAsynchronousDownloadService.list(confDocumentExportInfo1, SecurityConstants.FROM_IN).getData());

		confDocumentExportInfo1.setDocHandleState(DocumentHandleStateType.WAITING.getType());
		documentHandleInfos.addAll(remoteAsynchronousDownloadService.list(confDocumentExportInfo1, SecurityConstants.FROM_IN).getData());

		if (documentHandleInfos!=null && documentHandleInfos.size()>0) {
			for (ConfDocumentExportInfo documentHandleInfo : documentHandleInfos) {
				// 修改导出处理，进入队列，等待被处理  0=暂未处理,1=处理中,2=处理成功,3=处理异常,4=进入队列中，等待被处理
				if(documentHandleInfo.getDocHandleState() != 4){
					ConfDocumentExportInfo confDocumentExportInfo = new ConfDocumentExportInfo();
					confDocumentExportInfo.setId(documentHandleInfo.getId());
					confDocumentExportInfo.setDocHandleState(DocumentHandleStateType.WAITING.getType());
					remoteAsynchronousDownloadService.uploadDocumentExportInfo(confDocumentExportInfo, SecurityConstants.FROM_IN);
				}
				documentHandleInfoQueue.add(documentHandleInfo);
				futureContainer.add(documentExportHandlerService.submit(this::handleDocumentExport));
			}
		}

		while (true) {
			boolean isChecked = false;
			// 2、检查队列中的任务是否都已经完成
			if(futureContainer.size()>0){
				Iterator<Future<Void>> iterator = futureContainer.iterator();
				while (iterator.hasNext()) { // 查看是否有元素
					Future<Void> future = iterator.next();
					if(future.isDone()){
						iterator.remove();
					}
				}
				if(!isChecked && futureContainer.size() < workers){
					isChecked =  true;
				}
				try {
					log.info("当前文档导出队列大小：{}, 当前执行线程数: {}", futureContainer.size(), workers);
					Thread.currentThread().sleep(1000*5); //  防止线程，过度消耗资源
				} catch (InterruptedException e) {
					log.error("", e);
				}
			}else{
				isChecked =  true;
			}
			if(isChecked){
				// 3、当次批所有的文档导出请求都被处理，重新获取，未处理的文档导出请求
				ConfDocumentExportInfo confDocumentExportInfo2 = new ConfDocumentExportInfo();
				confDocumentExportInfo2.setServiceCode(this.serviceCode);
				confDocumentExportInfo2.setDocHandleState(DocumentHandleStateType.UNHANDLED.getType());
				documentHandleInfos = remoteAsynchronousDownloadService.list(confDocumentExportInfo2, SecurityConstants.FROM_IN).getData();

				if (documentHandleInfos!=null && documentHandleInfos.size()>0) {
					for (ConfDocumentExportInfo documentHandleInfo : documentHandleInfos) {
						ConfDocumentExportInfo confDocumentExportInfo = new ConfDocumentExportInfo();
						confDocumentExportInfo.setId(documentHandleInfo.getId());
						confDocumentExportInfo.setDocHandleState(DocumentHandleStateType.WAITING.getType());
						remoteAsynchronousDownloadService.uploadDocumentExportInfo(confDocumentExportInfo, SecurityConstants.FROM_IN);

						documentHandleInfoQueue.add(documentHandleInfo);
						futureContainer.add(documentExportHandlerService.submit(this::handleDocumentExport));
					}
					log.info("当前文档导出队列大小："+futureContainer.size()+", 新增个数："+documentHandleInfos.size()+" , 当前执行线程数："+workers);
					isChecked =  false;
				} else {
					log.info("当前文档导出队列大小："+futureContainer.size()+", 新增个数：0 ,当前执行线程数："+workers);
					try {
						Thread.currentThread().sleep(1000*10);  //  防止线程，过度消耗资源
					} catch (InterruptedException e) {
						log.error("", e);
					}
				}
			}
		}
	}

	public Void handleDocumentExport(){
		ConfDocumentExportInfo documentHandleInfo = documentHandleInfoQueue.poll();
		// 1、获取处理类，处理方法名称
		Integer documentHandleInfoId = documentHandleInfo.getId();
		String serviceName = documentHandleInfo.getServiceName();
		String exportFileName = documentHandleInfo.getExportFileName();
		String moduleName = documentHandleInfo.getModuleName();
		String exportButtomName = documentHandleInfo.getExportBtnName();
		Integer docHandleType = documentHandleInfo.getDocHandleType();
		String filePath = basePath + File.separator + exportFileName;
		documentHandleInfo.setExportFileName(filePath);

		int docHandleState = documentHandleInfo.getDocHandleState();
		// 要是状态为”处理中“，”处理异常“，需要先清理文件(0=暂未处理,1=处理中,2=处理成功,3=处理异常,4=进入队列中，等待被处理)
		if(docHandleState == 1 || docHandleState == 3 ){
			File doc = new File(basePath+exportButtomName);
			if(doc!=null && doc.exists()){
				doc.deleteOnExit();
			}
		}
		long currentTime = 0 ;
		long spentTime = 0;
		ConfDocumentExportInfo confDocumentExportInfo = new ConfDocumentExportInfo();
		try {

			// 3、执行类中处理方法
			// 3.1、更新状态“处理中”
			confDocumentExportInfo.setId(documentHandleInfoId);
			confDocumentExportInfo.setDocHandleState(DocumentHandleStateType.PROCESSING.getType());
			remoteAsynchronousDownloadService.uploadDocumentExportInfo(confDocumentExportInfo, SecurityConstants.FROM_IN);

			log.info("文档导出处理状态：服务名称["+serviceName+"]"+"-模块名称["+moduleName+"]-导出按钮["+exportButtomName+"]-文件名称["+exportFileName+"]开始执行！");
			currentTime = System.currentTimeMillis();
			// 2、从Spring IOC容器中获取处理对象
			DocumentExportHandler handler = (DocumentExportHandler)applicationContext.getBean(DocumentHandlerType.getDocumentHandlerEnumValue(docHandleType));
			handler.handle(documentHandleInfo);

			spentTime =  System.currentTimeMillis()-currentTime;
			// 3.2、更新状态“处理成功”

			confDocumentExportInfo.setId(documentHandleInfoId);
			confDocumentExportInfo.setDocHandleState(DocumentHandleStateType.SUCCESS.getType());
			confDocumentExportInfo.setDocHandleEndTime(LocalDateTime.now());
			remoteAsynchronousDownloadService.uploadDocumentExportInfo(confDocumentExportInfo, SecurityConstants.FROM_IN);
			log.info("文档导出处理状态：服务名称["+serviceName+"]"+"-模块名称["+moduleName+"]-导出按钮["+exportButtomName+"]-文件名称["+exportFileName+"]-消耗时长["+spentTime+"ms/"+(spentTime/1000)+"s],执行成功！");
		} catch (Exception e){
			//3.3、更新状态“处理异常”
			log.error("文档导出处理状态：服务名称["+serviceName+"]"+"-模块名称["+moduleName+"]-导出按钮["+exportButtomName+"],执行异常！", e);
			confDocumentExportInfo.setDocHandleState(DocumentHandleStateType.ERROR.getType());
			confDocumentExportInfo.setDocHandleError(e.getMessage());
			remoteAsynchronousDownloadService.uploadDocumentExportInfo(confDocumentExportInfo, SecurityConstants.FROM_IN);
		}
		return null;
	}


}
